package edu.uta.cfl.engine;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;

import edu.uta.cfl.data.Combo;
import edu.uta.cfl.data.Component;
import edu.uta.cfl.data.SUT;
import edu.uta.cfl.data.SUT.SUTState;
import edu.uta.cfl.util.Constants;

public class SUTManager {

	public static double getTCSpc(SUT sut, int[] tc, int len) {
		return TestCaseManager.getFailTCSpc(tc,
				sut.getSuspiciousComponents(len), sut.getParameters());
	}

	public static void setRank(SUT sut, int len) {
		ArrayList<Double> l1 = new ArrayList<Double>();
		for (Combo cmb : sut.getSuspiciousCombos(len)) {
			if (!l1.contains(cmb.getInducingPrb()))
				l1.add(cmb.getInducingPrb());
		}
		Collections.sort(l1);
		for (Combo cmb : sut.getSuspiciousCombos(len))
			cmb.setIPRank(l1.size() - l1.indexOf(cmb.getInducingPrb()));

		for (int i = 1; i <= l1.size(); i++) {
			ArrayList<Combo> cmbs = new ArrayList<Combo>();
			for (Combo cmb : sut.getSuspiciousCombos(len))
				if (cmb.getIPRank() == i)
					cmbs.add(cmb);
			rankBaseSpc(cmbs);
		}

	}

	public static void rankBaseSpc(ArrayList<Combo> cmbs) {
		ArrayList<Double> l1 = new ArrayList<Double>();
		ArrayList<Double> l2 = new ArrayList<Double>();

		for (Combo cmb : cmbs) {
			if (!l1.contains(cmb.getRankValue()))
				l1.add(cmb.getRankValue());

			if (!l2.contains(cmb.getEnvRankValue()))
				l2.add(cmb.getEnvRankValue());
		}

		Collections.sort(l1);
		Collections.sort(l2);
		ArrayList<Integer> l3 = new ArrayList<Integer>();
		for (Combo cmb : cmbs) {
			int idx = (l1.size() - 1 - l1.indexOf(cmb.getRankValue()))
					+ l2.indexOf(cmb.getEnvRankValue());
			if (!l3.contains(idx))
				l3.add(idx);
		}

		Collections.sort(l3);
		for (Combo cmb : cmbs) {
			int idx = (l1.size() - 1 - l1.indexOf(cmb.getRankValue()))
					+ l2.indexOf(cmb.getEnvRankValue());

			cmb.setGroupRank(l3.indexOf(idx) + 1);
		}

	}

	public static ArrayList<Combo> getComboByRank(SUT sut, int len, int rank) {
		ArrayList<Combo> output = new ArrayList<Combo>();
		for (Combo cmb : sut.getSuspiciousCombos(len))
			if (cmb.getRank() == rank)
				output.add(cmb);
		return output;
	}

	public static ArrayList<Combo> getTopNCombo(SUT sut, int len, int n) {
		ArrayList<Combo> output = new ArrayList<Combo>();
		int rank = 1;
		ArrayList<Combo> cmbs = new ArrayList<Combo>();
		cmbs = getComboByRank(sut, len, rank++);
		while (output.size() < n && cmbs.size() > 0) {
			output.addAll(cmbs);
			cmbs = getComboByRank(sut, len, rank++);
		}

		while (output.size() > n)
			output.remove(output.size() - 1);

		return output;
	}

	public static ArrayList<int[]> generateNTC(SUT sut, int len) {
		ArrayList<int[]> output = new ArrayList<int[]>();
		ArrayList<int[]> outputTC = new ArrayList<int[]>();
		ArrayList<Combo> cmbs = new ArrayList<Combo>();

		// int i=0; // the most suspicious combo
		for (int i = 0; i < Constants.susCmbNum
				&& i < sut.getSuspiciousCombos(len).size(); i++) {
			// {{ for each combo
			output.addAll(generateNTCCombo(sut, sut.getSuspiciousCombos(len)
					.get(i), output, len));
			cmbs.add(sut.getSuspiciousCombos(len).get(i));
		}
		sut.setTestGen4TheseCmb(cmbs);
		return output;
	}

	public static ArrayList<int[]> generateNTCCombo(SUT sut, Combo cmb,
			ArrayList<int[]> preLst, int len) {
		int numtc = 0;
		// boolean finICombo = true;

		/*
		 * if (!sut.getConstraints().isEmpty()){
		 * sut.getChoco().initChoco(sut.getParameters(),sut.getConstraints()); }
		 */
		ArrayList<int[]> output = new ArrayList<int[]>();
		ArrayList<int[]> outputTC = new ArrayList<int[]>();
		Random random = new Random(100);
		while (sut.getStatus() != SUTState.failureInducing) {
			int[] cmbArray = new int[sut.getParameters().size()];
			int[] tc = new int[sut.getParameters().size()];
			tc = ComboManager.getArrayCombo(cmb, sut.getParameters());
			cmbArray = Arrays.copyOf(tc, tc.length);
			ArrayList<Component> cmps = new ArrayList<Component>();

			cmps = sut.getSuspiciousComponents(len);

			int[] prmNMin = new int[sut.getParameters().size()];
			Arrays.fill(prmNMin, 0);

			int itrNum = 0;
			for (int l = 0; l < tc.length; l++)
				if (tc[l] == -1) {
					tc[l] = ComponentManager.getNMinRankVal(sut.getParameters()
							.get(l), cmps, ++prmNMin[l]);
					if (!sut.getConstraints().isEmpty()) {
						itrNum = 0;
						while (!sut.getChoco().isSatisfied(tc, 0)
								&& itrNum < Constants.MaxNumGenTsNew) {
							tc[l] = ComponentManager
									.getNMinRankVal(sut.getParameters().get(l),
											cmps, ++prmNMin[l]);
							itrNum++;
						}
					}
				}

			int itr = 0;
			boolean cont = true;
			if (itrNum == Constants.MaxNumGenTsNew)
				cont = false;
			// Update tc randomly if it is not new Constants.MaxNumGenTsNew
			// times
			while (cont && itr < Constants.MaxNumGenTsValid) {
				if (!TestCaseManager.isTCExist(tc, sut.getTs().getMatrix())
						&& !TestCaseManager.isTCExist(tc, outputTC)
						&& !TestCaseManager.isTCExist(tc, preLst))
					cont = false;
				if (cont) {
					int idx;
					boolean flag = false;
					do {
						flag = false;
						do {
							idx = (int) (random.nextInt(sut.getParameters()
									.size()));
						} while (cmbArray[idx] != -1);

						int val = ComponentManager.getNMinRankVal(sut
								.getParameters().get(idx), cmps,
								prmNMin[idx] + 1);
						int origval = tc[idx];
						if (val != -1) {
							tc[idx] = val;
							prmNMin[idx]++;
							if (!sut.getConstraints().isEmpty()) {
								itrNum = 0;
								while (!sut.getChoco().isSatisfied(tc, 0)
										&& itrNum < Constants.MaxNumGenTsNew) {
									val = ComponentManager.getNMinRankVal(sut
											.getParameters().get(idx), cmps,
											++prmNMin[idx]);
									if (val != -1) {
										tc[idx] = val;
									}

									else
										itrNum = Constants.MaxNumGenTsNew;
									itrNum++;
								}
							}
							if (itrNum > Constants.MaxNumGenTsNew) {
								flag = true;
								tc[idx] = origval;
							}
						}
					} while (flag);
					itr++;
				}
			}

			if (itr < Constants.MaxNumGenTsValid) {
				int[] tcit = new int[sut.getParameters().size() + 1];
				Arrays.fill(tcit, itr);
				for (int l = 0; l < tc.length; l++)
					tcit[l] = tc[l];
				output.add(tcit);
				outputTC.add(tc);
				numtc++;
			} else {

				if (numtc == 0) {
					cmb.setFailInd(true);
					sut.setStatus(SUTState.failureInducing);
				} else
					return output;
			}

			if (numtc == Constants.tsCmbNum)
				return output;
		}
		// }}
		return output;

	}

	// check if generating new test case is possible if add this list to test
	// set
	public static boolean isNewTCPossible(Combo cmb, ArrayList<int[]> tcLst,
			SUT sut) {
		int frTCLst = ComboManager.comboFrequency(cmb, tcLst,
				sut.getParameters());
		int frSUT = ComboManager.comboFrequency(cmb, sut.getTs().getMatrix(),
				sut.getParameters());
		int maxFr = ComboManager.possibleTC(cmb, sut.getParameters());
		if (frSUT + frTCLst == maxFr)
			return false;
		else
			return true;
	}

	public static void approach(SUT sut, int cmbLength, int Approach) {

		if (Approach == Constants.APPROACH_LOAD) {
			if (sut.getTs().getAllFailTC().size() == 0)
				sut.setStatus(SUTState.notFound);
			fillValidInvalidLst(sut, cmbLength, Constants.COMBO_ALL);
		}

		sut.getSuspiciousCombos(cmbLength).removeAll(
				sut.getSuspiciousCombos(cmbLength));
		sut.getSuspiciousComponents(cmbLength).removeAll(
				sut.getSuspiciousComponents(cmbLength));

		defineRankSpcCombo(sut, sut.getValidCombos(cmbLength), cmbLength);

		if (sut.getSuspiciousCombos(cmbLength).size() == 0) {
			if (Constants.maxIndStr == cmbLength - 1)
				sut.setStatus(SUTState.InducingTest);
			else
				sut.setStatus(SUTState.expansion);
		}

	}

	public static void sortSpcCombosByRank(SUT sut, int cmbLength) {

		ArrayList<Combo> cmbs = new ArrayList<Combo>();

		int length = (int) (Math.log10(sut.getSuspiciousCombos(cmbLength)
				.size()) + 1);
		int factor = (int) Math.pow(10, length);
		ArrayList<Integer> l1 = new ArrayList<Integer>();
		for (Combo cmb : sut.getSuspiciousCombos(cmbLength))
			if (!l1.contains(cmb.getIPRank() * factor + cmb.getGroupRank()))
				l1.add(cmb.getIPRank() * factor + cmb.getGroupRank());

		Collections.sort(l1);
		for (Combo cmb : sut.getSuspiciousCombos(cmbLength)) {
			cmb.setRank(l1.indexOf(cmb.getIPRank() * factor
					+ cmb.getGroupRank()) + 1);
		}

		int i = 1;
		do {
			for (Combo cmb : sut.getSuspiciousCombos(cmbLength)) {
				if (cmb.getRank() == i)
					cmbs.add(cmb);
			}
			i++;
		} while (cmbs.size() != sut.getSuspiciousCombos(cmbLength).size());

		sut.getSuspiciousCombos(cmbLength).clear();
		sut.getSuspiciousCombos(cmbLength).addAll(cmbs);
	}

	public static void deriveSmallerCombo(SUT sut) {
		ArrayList<Combo> allCombos = new ArrayList<Combo>();
		int len = sut.getStrength();
		do {
			allCombos = ComboManager.findFixLenCombosTests(sut.getParameters(),
					sut.getTs().getAllFailTC(), --len);

			ArrayList<Combo> validCmbs = new ArrayList<Combo>();
			ArrayList<Combo> invalidCmbs = new ArrayList<Combo>();
			if (!sut.getConstraints().isEmpty()) {
				if (len == sut.getStrength() - 1) {
					validCmbs = sut.getValidCombos(len);
					invalidCmbs = sut.getInvalidCombos(len);
				} else {
					ArrayList<Combo> allSuperCombos = new ArrayList<Combo>();
					allSuperCombos = ComboManager.findFixLenCombosTests(
							sut.getParameters(), sut.getTs().getAllFailTC(),
							len + 1);
					for (Combo c : allSuperCombos) {
						if (sut.getChoco().isSatisfied(c, 0))
							validCmbs.add(c);
					}
				}
			}

			for (Combo cmb : allCombos) {
				int frSuperCmbLst = 0;
				if (!sut.getConstraints().isEmpty())
					frSuperCmbLst = ComboManager.cmbFrSupCmbLst(cmb, validCmbs);
				else
					frSuperCmbLst = ComboManager.possibleCombo(cmb,
							sut.getParameters());

				if (frSuperCmbLst == ComboManager.cmbFrSupCmbLst(cmb,
						sut.getSuspiciousCombos(len + 1))) {
					sut.getSuspiciousCombos(len).add(cmb);
				}
			}

			ArrayList<Integer> l1 = new ArrayList<Integer>();
			for (Combo cmb : sut.getSuspiciousCombos(len)) {
				int maxRank = ComboManager.maxRankSubCmb(cmb,
						sut.getSuspiciousCombos(sut.getStrength()));
				if (!l1.contains(maxRank))
					l1.add(maxRank);
			}
			Collections.sort(l1);
			for (Combo cmb : sut.getSuspiciousCombos(len)) {
				int idx = l1.indexOf(ComboManager.maxRankSubCmb(cmb,
						sut.getSuspiciousCombos(sut.getStrength())));
				cmb.setRank(idx + 1);
			}
			sortSpcCombosByRank(sut, len);

		} while (allCombos.size() == 0 || len == 0);

	}

	public static ArrayList<int[]> generateNewTC(SUT sut, int len) {

		ArrayList<int[]> tcList = new ArrayList<int[]>();
		if (sut.getStatus() == SUTState.continuing) {
			tcList = SUTManager.generateNTC(sut, len);
		} else if (sut.getStatus() == SUTState.failureInducing)
			sut.getSuspiciousCombos(sut.getStrength()).get(1).setFailInd(true);

		if (!(sut.getStatus() == SUTState.continuing))
			SUTManager.deriveSmallerCombo(sut);

		return tcList;

	}

	public static void expandCmb(SUT sut, int len) {

		sut.newCmbCmpLst(++len);

		fillValidInvalidLst(sut, len, Constants.COMBO_EXIST);
		defineRankSpcCombo(sut, sut.getValidCombos(len), len);

		if (!(sut.getSuspiciousCombos(len).size() == 0))
			sut.setStatus(SUTState.continuing);

	}

	public static void defineRankSpcCombo(SUT sut, ArrayList<Combo> validCmbs,
			int cmbLength) {
		// find all suspicious combo with cmblength
		for (Combo cmb : validCmbs)
			if (ComboManager.isComboSpc(cmb, sut.getTs(), sut.getParameters())) {
				cmb.setInducingPrb(ComboManager.cmbInducingPrb(cmb,
						sut.getTs(), sut.getParameters()));
				sut.addSpcCombo(cmb);

				for (Component spcCmp : cmb.getComponent())
					sut.addSpcComponent(spcCmp, cmbLength);
			}

		// find suspicious number for all suspicious component
		for (Component spcCmp : sut.getSuspiciousComponents(cmbLength)) {
			int frFailTS = ComponentManager.compFrequencyTCLst(spcCmp, sut
					.getTs().getAllFailTC(), sut.getParameters());
			int frALLTS = ComponentManager.compFrequencyTCLst(spcCmp, sut
					.getTs().getMatrix(), sut.getParameters());
			spcCmp.setPart1((double) frFailTS / frALLTS);
			spcCmp.setPart2((double) frFailTS
					/ sut.getTs().getAllFailTC().size());

			int frSpcCmp = ComponentManager.compFrequencyCmb(spcCmp,
					sut.getSuspiciousCombos(cmbLength));
			int spcCmpLen = sut.getSuspiciousCombos(cmbLength).size();
			spcCmp.setPart3((double) frSpcCmp / spcCmpLen);
		}

		// find suspicious number of environment for combo
		for (Combo spcCmb : sut.getSuspiciousCombos(cmbLength)) {
			double rank = 100000;
			for (int[] flTC : sut.getTs().getAllFailTC()) {
				if (ComboManager.isCmbInTC(spcCmb, flTC, sut.getParameters())
						&& SUTManager.getTCSpc(sut, flTC, cmbLength) < rank)
					rank = SUTManager.getTCSpc(sut, flTC, cmbLength);
			}

			for (Component spcCmp : spcCmb.getComponent())
				rank -= spcCmp.getRankValue();

			rank /= (sut.getParameters().size() - spcCmb.getComponent().size());
			spcCmb.setEnvRankValue(rank);
			spcCmb.updateRankValue();

		}

		SUTManager.setRank(sut, cmbLength);
		sortSpcCombosByRank(sut, cmbLength);
	}

	public static void fillValidInvalidLst(SUT sut, int cmbLength, int type) {
		ArrayList<Combo> validCmbs = new ArrayList<Combo>();
		ArrayList<Combo> invalidCmbs = new ArrayList<Combo>();

		if (type == Constants.COMBO_ALL) {
			ArrayList<Combo> allCombos = ComboManager.findFixLenCombosALL(
					sut.getParameters(), cmbLength);
			if (!sut.getConstraints().isEmpty()) {
				sut.getChoco().initChoco(sut.getParameters(),
						sut.getConstraints());
				for (Combo c : allCombos) {
					if (sut.getChoco().isSatisfied(c, 0))
						validCmbs.add(c);
					else
						invalidCmbs.add(c);
				}
			} else
				validCmbs = allCombos;

			sut.setInvalidCombos(invalidCmbs, cmbLength);
			sut.setValidCombos(validCmbs, cmbLength);
		} else {
			ArrayList<Combo> allCombos = ComboManager.findFixLenCombosTests(
					sut.getParameters(), sut.getTs().getAllFailTC(), cmbLength);
			if (!sut.getConstraints().isEmpty()) {
				sut.getChoco().initChoco(sut.getParameters(),
						sut.getConstraints());
				for (Combo c : allCombos) {
					if (sut.getChoco().isSatisfied(c, 0))
						validCmbs.add(c);
					else
						invalidCmbs.add(c);
				}
			} else
				validCmbs = allCombos;

			sut.setInvalidCombos(invalidCmbs, cmbLength);
			sut.setValidCombos(validCmbs, cmbLength);
		}

	}
}
